﻿
module Octopus.VMDeployer.Functions

open System.Management
open Octopus.VMDeployer.Utility


let scheduleFunc (milliseconds:int) (foo:unit -> unit) =
  let f() = System.Threading.Thread.Sleep(milliseconds); foo()
  Async.Start (async { f() })


let tryExecuteFunc (foo:unit -> unit) =
  try foo()
  with _ -> System.Threading.Thread.Sleep(5000); foo()


let octopus_notifyHpcNodeReady (vmGuid:System.Guid) (securityToken:System.Guid) (addresses:string array) =
  let f() = Utility.tryExecute_remote_Octopus_function addresses [| "notifyHpcNodeReady"; string vmGuid; string securityToken; |]
  tryExecuteFunc f

let octopus_notifyConfigurationFailed (vmGuid:System.Guid) (securityToken:System.Guid) (addresses:string array) message =
  let f() = Utility.tryExecute_remote_Octopus_function addresses [| "notifyConfigurationFailed"; string vmGuid; message |]
  tryExecuteFunc f

let octopus_notifyVmConfigured (vmGuid:System.Guid) (securityToken:System.Guid) (addresses:string array) message =
  let f() = Utility.tryExecute_remote_Octopus_function addresses [| "notifyVmConfigured"; string vmGuid; message |]
  tryExecuteFunc f

let octopus_upgradeNic (vmGuid:System.Guid) (securityToken:System.Guid) (addresses:string array) =
  let f() = Utility.tryExecute_remote_Octopus_function addresses [| "upgradeNic"; string vmGuid |]
  tryExecuteFunc f



//--------------------------------------------------



type Win32ShutdownFlags =
  | LogOff = 0x0
  | LogOffForced = 0x4
  | Shutdown = 0x1
  | ShutdownForced = 0x5
  | Reboot = 0x2
  | RebootForced = 0x6
  | PowerOff = 0x8
  | PowerOffForced = 0xC


// Minimum supported client Windows 2000 Professional 
// Minimum supported server Windows 2000 Server 
let win32Shutdown (flags:Win32ShutdownFlags) =
  let scope = new ManagementScope(@"root\cimv2", null)
  use imageService = Utility.getServiceObject scope "Win32_OperatingSystem"
  use inParams = imageService.GetMethodParameters("Win32Shutdown")
  inParams.["Flags"] <- (int flags)
  inParams.["Reserved"] <- null

  // http://msdn.microsoft.com/en-us/library/aa394058(VS.85).aspx
  use outParams = imageService.InvokeMethod("Win32Shutdown", inParams, null)

  let code = outParams.["ReturnValue"] :?> uint32
  if code = uint32(Utility.ReturnCode.Completed) then ()
  else if code = uint32(Utility.ReturnCode.Started) then Utility.jobCompleted outParams scope
  else failwithf "Unable to shutdown the computer. Error %d" code


// Minimum supported client Windows Vista 
// Minimum supported server Windows Server 2008 
let win32ShutdownTracker (flags:Win32ShutdownFlags, comment:string, secondsTimeout:int) =
  let scope = new ManagementScope(@"root\cimv2", null)
  use imageService = Utility.getServiceObject scope "Win32_OperatingSystem"
  use inParams = imageService.GetMethodParameters("Win32ShutdownTracker")
  inParams.["Flags"] <- (int flags)
  inParams.["Comment"] <- comment
  inParams.["ReasonCode"] <- 0
  inParams.["Timeout"] <- secondsTimeout

  // http://msdn.microsoft.com/en-us/library/aa394057(VS.85).aspx
  use outParams = imageService.InvokeMethod("Win32ShutdownTracker", inParams, null)

  let code = outParams.["ReturnValue"] :?> uint32
  if code = uint32(Utility.ReturnCode.Completed) then ()
  else if code = uint32(Utility.ReturnCode.Started) then Utility.jobCompleted outParams scope
  else failwithf "Unable to shutdown the computer. Error %d" code



// Minimum supported client Windows 2000 Professional 
// Minimum supported server Windows 2000 Server 
let changeAccountPassword (username:string) (newPassword:string) =
  let o = System.Runtime.InteropServices.Marshal.BindToMoniker(sprintf "WinNT://./%s,user" username)
  let t = o.GetType()
  // http://msdn.microsoft.com/en-us/library/aa746344(VS.85).aspx
  t.InvokeMember("SetPassword", System.Reflection.BindingFlags.InvokeMethod, null, o, [| newPassword |] ) |> ignore



// Minimum supported client Windows XP
// Minimum supported server Windows Server 2003
let changeRSOP_SecuritySetting (keyname:string) (setting:bool) =
  // http://msdn.microsoft.com/en-us/library/aa375062(VS.85).aspx
  let query = sprintf @"select * from RSOP_SecuritySettingBoolean where KeyName='%s'" keyname
  let scope = new ManagementScope(@"root\RSOP\computer", null)
  let obj = Utility.get_WmiObject2 scope query
  
  if obj = null then failwithf "Unable to find the security setting '%s'" keyname
  else
    let b = obj.["Setting"] :?> bool
    if b <> setting then
      obj.["Setting"] <- setting
      obj.Put() |> ignore



// Minimum supported client Windows XP 
// Minimum supported server Windows Server 2003 
let renameComputername (newName:string) =
  let scope = new ManagementScope(@"root\cimv2", null)
  use imageService = Utility.getServiceObject scope "Win32_ComputerSystem"
  use inParams = imageService.GetMethodParameters("Rename")
  inParams.["Name"] <- newName
  inParams.["UserName"] <- null
  inParams.["Password"] <- null

  // http://msdn.microsoft.com/en-us/library/aa393056(VS.85).aspx
  use outParams = imageService.InvokeMethod("Rename", inParams, null)

  let code = outParams.["ReturnValue"] :?> uint32
  if code = uint32(Utility.ReturnCode.Completed) then ()
  else if code = uint32(Utility.ReturnCode.Started) then Utility.jobCompleted outParams scope
  else failwithf "Unable to change the name. Error %d" code


// Minimum supported client Windows XP 
// Minimum supported server Windows Server 2003 
let unjoinDomain () =
  let scope = new ManagementScope(@"root\cimv2", null)
  use imageService = Utility.getServiceObject scope "Win32_ComputerSystem"
  use inParams = imageService.GetMethodParameters("UnjoinDomainOrWorkgroup")
  inParams.["FUnjoinOptions"] <- 0
  inParams.["UserName"] <- null
  inParams.["Password"] <- null

  // http://msdn.microsoft.com/en-us/library/aa393942(VS.85).aspx
  use outParams = imageService.InvokeMethod("UnjoinDomainOrWorkgroup", inParams, null)

  let code = outParams.["ReturnValue"] :?> uint32
  if code = uint32(Utility.ReturnCode.Completed) then ()
  else if code = uint32(Utility.ReturnCode.Started) then Utility.jobCompleted outParams scope
  else failwithf "Unable to unjoin the domain/workgroup. Error %d" code


// Minimum supported client Windows XP 
// Minimum supported server Windows Server 2003 
let joinDomain (domain:string) (username:string, password:string) =
  let scope = new ManagementScope(@"root\cimv2", null)
  use imageService = Utility.getServiceObject scope "Win32_ComputerSystem"
  use inParams = imageService.GetMethodParameters("JoinDomainOrWorkgroup")
  inParams.["Name"] <- domain
  inParams.["Password"] <- password
  inParams.["UserName"] <- (sprintf "%s\\%s" domain username)
  inParams.["FJoinOptions"] <- (1 ||| 2)

  // http://msdn.microsoft.com/en-us/library/aa392154(VS.85).aspx
  use outParams = imageService.InvokeMethod("JoinDomainOrWorkgroup", inParams, null)

  let code = outParams.["ReturnValue"] :?> uint32
  if code = uint32(Utility.ReturnCode.Completed) then ()
  else if code = uint32(Utility.ReturnCode.Started) then Utility.jobCompleted outParams scope
  else failwithf "Unable to join the domain/workgroup. Error %d" code


// Moved inside Utility.fs
//let renewDCHPAddresses () =
